/*
 * About this example
 *
 * 1. Start with initializing led module:
 *    a. Set the timer of led first, this determines the frequency
 *       and resolution of PWM.
 *    b. Then set the led channel you want to use,
 *       and bind with one of the timers.
 *
 * 2. You need first to install a default fade function,
 *    then you can use fade APIs.
 *
 * 3. You can also set a target duty directly without fading.
 *
 * 4. This example uses GPIO18/19/4/5 as led output,
 *    and it will change the duty repeatedly.
 *
 * 5. GPIO18/19 are from high speed channel group.
 *    GPIO4/5 are from low speed channel group.
 *
 */
#include <stdio.h>
#include <string.h>
// #include "esp_system.h"
// #include "esp_chip_info.h"
#include "esp_flash.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_log.h"
// #include "esp_ota_ops.h"
#include "esp_mac.h"
#include "nvs_value.h"
#include "radar.h"
#include "main.h"
#include "gatt_server.h"
#include "lamp_ctrl.h"
#include "ws2812.h"
#include "h03_config.h"

#define TAG "[NVS]"

// #define DEBUG_MODE  1

static uint8_t new_user_flag;

static char rec_day[4] = {0};

void nvs_record_error(bool bit, uint8_t pos)
{
    if(bit == 1){
        system_state.error_now |= (1 << pos);
    }else{
        system_state.error_now &= ~(1 << pos);
    }
    if(bit == 1){
        uint16_t error_ex_tmp = 0;
        error_ex_tmp |= (1 << pos);
        if(error_ex_tmp != system_state.error_ex){
            system_state.error_ex = error_ex_tmp;
            paramenter_setting_u16("error", system_state.error_ex);
            ESP_LOGE(TAG, "save error code:%d", system_state.error_ex);
        }
    }
}

void paramenter_setting_str(const char *flag, const char *data)
{
    nvs_handle_t my_handle;
	nvs_open("storage", NVS_READWRITE, &my_handle);
	nvs_set_str(my_handle, flag, data);
    nvs_commit(my_handle);
    nvs_close(my_handle);
}

void paramenter_setting_u32(const char *flag, uint32_t data)
{
    nvs_handle_t my_handle;
	nvs_open("storage", NVS_READWRITE, &my_handle);
	nvs_set_u32(my_handle, flag, data);
    nvs_commit(my_handle);
    nvs_close(my_handle);
}

void paramenter_setting_u16(const char *flag, uint16_t data)
{
    nvs_handle_t my_handle;
	nvs_open("storage", NVS_READWRITE, &my_handle);
	nvs_set_u16(my_handle, flag, data);
    nvs_commit(my_handle);
    nvs_close(my_handle);
}

void paramenter_setting_u8(const char *flag, uint8_t data)
{
    nvs_handle_t my_handle;
	nvs_open("storage", NVS_READWRITE, &my_handle);
	nvs_set_u8(my_handle, flag, data);
    nvs_commit(my_handle);
    nvs_close(my_handle);
}

static void paramenter_factory_setting(nvs_handle_t handle, uint8_t lv, bool update_flag)
{
    if(lv >= 4){
        ESP_LOGI(TAG, "restore factory settings! ");
        nvs_set_str(handle, "bt_mac", "NULL");
        nvs_set_str(handle, "wifi_ssid", "NULL");
        nvs_set_str(handle, "wifi_password", "NULL");
        nvs_set_str(handle, "brokerurl", BROKERURL);
        nvs_set_str(handle, "acckey", "hjy");
        nvs_set_str(handle, "secretkey", "hjy123");
        nvs_set_u8(handle, "nvs_ver", 1);
        nvs_set_u8(handle, "s_ota", 0);
        nvs_set_str(handle, "hw_ver", HARDWARE_VERSION);
        nvs_set_u16(handle, "error", 0);
        nvs_set_u8(handle, "activation", 0);
        nvs_set_u8(handle, "touch_mode", 1);
    }
    if(lv >= 3){
        nvs_set_str(handle, "brokerurluser", "mqtt://mq-iot-h03.hylgeai.cn:1883");
        nvs_set_u8(handle, "mqttveruser", 0);
        nvs_set_str(handle, "groupiduser", "GID_HJY_MQTTX");
        nvs_set_str(handle, "acckeyuser", "LTAI5t92a8U65Zgh9QtBFmaJ");
        nvs_set_str(handle, "secretkeyuser", "XYaoqJousixSUzCN2el9YkQ48mGwLr");
        nvs_set_u8(handle, "newuser", 1);
        nvs_set_u8(handle, "mqttwork", 1);
        nvs_set_u8(handle, "decode", 1);
        nvs_set_u32(handle, "rpt_start", 0);
        nvs_set_str(handle, "rec_day", "0");
    }
    if(lv >= 2){
        nvs_set_u8(handle, "initial", 0);
        nvs_set_u8(handle, "debug_mode", 0);
        nvs_set_u8(handle, "demo_mode", 0);
    }
    if(lv >= 1){
        nvs_set_u8(handle, "onoff_status", 0);
        nvs_set_u8(handle, "control_mode", 1);
        nvs_set_u8(handle, "brightness_val", 90);
        nvs_set_u8(handle, "color_temp", 127);
        nvs_set_u16(handle, "delay_time", 0);
        nvs_set_u8(handle, "gesture_mode", 0);
        nvs_set_u8(handle, "scene_mode", 1);
        nvs_set_u16(handle, "study_time1", 0);
        nvs_set_u16(handle, "study_time2", 0);
        nvs_set_u16(handle, "study_time3", 0);
        nvs_set_u16(handle, "study_time4", 0);
        nvs_set_u16(handle, "study_time5", 0);
        nvs_set_u16(handle, "study_time6", 0);
        nvs_set_u16(handle, "study_time7", 0);
    }
    nvs_set_str(handle, "device_model", DEVICE_MODEL);
	ESP_LOGI(TAG, "restore factory settings OK.");
}

void paramenter_restore(uint8_t level)
{
    nvs_handle_t my_handle;
    nvs_open("storage", NVS_READWRITE, &my_handle);
    paramenter_factory_setting(my_handle, level, 0);
    nvs_commit(my_handle);
    nvs_close(my_handle);
}

void study_time_reset(learning_duration_t *duration, char *day)
{    
    if(strcmp(day, rec_day) != 0){
        strcpy(rec_day, day);
        nvs_handle_t my_handle;
    nvs_open("storage", NVS_READWRITE, &my_handle);

    nvs_set_u16(my_handle, "study_time1", 0);
    nvs_set_u16(my_handle, "study_time2", 0);
    nvs_set_u16(my_handle, "study_time3", 0);
    nvs_set_u16(my_handle, "study_time4", 0);
    nvs_set_u16(my_handle, "study_time5", 0);
    nvs_set_u16(my_handle, "study_time6", 0);
    nvs_set_u16(my_handle, "study_time7", 0);
    nvs_set_str(my_handle, "rec_day", rec_day);

    nvs_commit(my_handle);
    nvs_close(my_handle);

        duration->mild = 0;
        duration->moderate = 0;
        duration->severe = 0;
        duration->present = 0;
        duration->single = 0;
        duration->last = 0;
        duration->now = 0;
        duration->sum = 0;
        duration->lighting = 0;

        ESP_LOGI(TAG,"reset the studytime");
    }
}

void nvs_save_studytime(learning_duration_t duration)
{
    ESP_LOGI(TAG, "nvs save studytime");
    nvs_handle_t my_handle;
    nvs_open("storage", NVS_READWRITE, &my_handle);

    nvs_set_u16(my_handle, "study_time1", duration.mild);
    nvs_set_u16(my_handle, "study_time2", duration.moderate);
    nvs_set_u16(my_handle, "study_time3", duration.severe);
    nvs_set_u16(my_handle, "study_time4", duration.lighting);
    nvs_set_u16(my_handle, "study_time5", duration.present);
    nvs_set_u16(my_handle, "study_time6", duration.last);
    nvs_set_u16(my_handle, "study_time7", duration.sum);

    nvs_commit(my_handle);
    nvs_close(my_handle);
}

void nvs_init(void)
{
    char buf[64];
    size_t buf_len;
    int ver_big;
    int ver_small;

    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
        ESP_LOGI(TAG, "nvs_flash_init failed (0x%x), erasing partition and retrying", err);
        const esp_partition_t* nvs_partition = esp_partition_find_first(
                ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
        assert(nvs_partition && "partition table must have an NVS partition");
        ESP_ERROR_CHECK( esp_partition_erase_range(nvs_partition, 0, nvs_partition->size) );
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK( err );

    nvs_handle_t my_handle;
    nvs_open("storage", NVS_READWRITE, &my_handle);

    memset(buf,0,64);
    buf_len = sizeof(buf);
    nvs_get_str(my_handle, "device_model", buf, &buf_len);
    ESP_LOGI(TAG,"device_model = %s", buf);
    if(strcmp(buf, DEVICE_MODEL) != 0){
        buf_len = sizeof(buf);
        memset(buf, 0, buf_len);
        nvs_get_str(my_handle, "wifi_ssid", buf, &buf_len);
        printf("wifi_ssid:%s\n", buf);
        if(strcmp(buf, "NULL") != 0){
            printf("This is an old device, upgrade now\n");
            paramenter_factory_setting(my_handle, 4, 1);
        }else{
        printf("this is a new device\n");
            nvs_erase_all(my_handle);
            paramenter_factory_setting(my_handle, 4, 0);
            device_attr.initial = 1;
        }
        nvs_commit(my_handle);
        nvs_close(my_handle);
        nvs_open("storage", NVS_READWRITE, &my_handle);
    }

    nvs_get_u8(my_handle, "initial", &device_attr.initial);
    printf("initial = %d\n", device_attr.initial);


    buf_len = sizeof(device_attr.hardware_version);
    memset(buf,0,buf_len);
    nvs_get_str(my_handle, "hw_ver", buf, &buf_len);
    strcpy(device_attr.hardware_version, buf);
    sscanf(device_attr.hardware_version, "v%d.%d", &ver_big, &ver_small);
    gatt_device_info[8] = ((ver_big & 0x0f) << 4) | (ver_small & 0x0f);
    printf("hardware_version = %s\n", device_attr.hardware_version);
    gatt_device_info[7] = 0x24;

    uint8_t nvs_ver = 0;
    nvs_get_u8(my_handle, "nvs_ver", &nvs_ver);
    if(nvs_ver == 1){
        nvs_set_u8(my_handle, "sending_mode", 0);
        nvs_set_u16(my_handle, "sending_period", 10);
        nvs_set_u8(my_handle, "nvs_ver", 2);
        ESP_LOGI(TAG,"updata nvs");
    }
    ESP_LOGI(TAG,"nvs_ver= %d", nvs_ver);

    nvs_get_u8(my_handle, "debug_mode", &device_attr.debug_mode);
    printf("debug_mode = %d\n", device_attr.debug_mode);

#ifdef DEBUG_MODE
    device_attr.debug_mode = 1;
#else
    nvs_get_u8(my_handle, "demo_mode", &device_attr.demo_mode);
#endif
    printf("demo_mode = %d\n", device_attr.demo_mode);
    if(device_attr.demo_mode == 1){
        esp_log_level_set("*", ESP_LOG_NONE);
    }else{
        if(device_attr.debug_mode == 2){
            esp_log_level_set("*", ESP_LOG_DEBUG);
        }else if(device_attr.debug_mode == 1){
            esp_log_level_set("*", ESP_LOG_INFO);
        }else if(device_attr.debug_mode == 0){
            esp_log_level_set("*", ESP_LOG_ERROR);
        }
    }
    
    buf_len = sizeof(device_attr.wifi_param.wifi_ssid);
    memset(device_attr.wifi_param.wifi_ssid, 0, buf_len);
    nvs_get_str(my_handle, "wifi_ssid", device_attr.wifi_param.wifi_ssid, &buf_len);
    ESP_LOGI(TAG,"wifi_ssid = %s", device_attr.wifi_param.wifi_ssid);

    buf_len = sizeof(device_attr.wifi_param.wifi_password);
    memset(device_attr.wifi_param.wifi_password, 0, buf_len);
    nvs_get_str(my_handle, "wifi_password", device_attr.wifi_param.wifi_password, &buf_len);
    ESP_LOGI(TAG,"wifi_password = %s", device_attr.wifi_param.wifi_password);

    nvs_get_u8(my_handle, "newuser", &new_user_flag);
    ESP_LOGI(TAG,"newuser = %d", new_user_flag);
    if(new_user_flag == 1){
        buf_len = sizeof(device_attr.mqtt_param.mqtt_broken_url);
        memset(buf,0,buf_len);
        nvs_get_str(my_handle, "brokerurluser", buf, &buf_len);
        strcpy(device_attr.mqtt_param.mqtt_broken_url,buf);
        ESP_LOGD(TAG,"brokerurl = %s", device_attr.mqtt_param.mqtt_broken_url);

        nvs_get_u8(my_handle, "mqttveruser", &device_attr.mqtt_param.mqttver);
        ESP_LOGD(TAG,"mqttveruser = %d", device_attr.mqtt_param.mqttver);

        buf_len = sizeof(device_attr.mqtt_param.user_groupid);
        memset(buf,0,buf_len);
        nvs_get_str(my_handle, "groupiduser", buf, &buf_len);
        strcpy(device_attr.mqtt_param.user_groupid, buf);
        ESP_LOGD(TAG,"groupiduser = %s", device_attr.mqtt_param.user_groupid);

        buf_len = sizeof(device_attr.mqtt_param.mqtt_broken_accesskey);
        memset(buf,0,buf_len);
        nvs_get_str(my_handle, "acckeyuser", buf, &buf_len);
        strcpy(device_attr.mqtt_param.mqtt_broken_accesskey,buf);
        ESP_LOGD(TAG,"acckey = %s", device_attr.mqtt_param.mqtt_broken_accesskey);

        buf_len = sizeof(device_attr.mqtt_param.mqtt_broken_secretkey);
        memset(buf,0,buf_len);
        nvs_get_str(my_handle, "secretkeyuser", buf, &buf_len);
        strcpy(device_attr.mqtt_param.mqtt_broken_secretkey,buf);
        ESP_LOGD(TAG,"secretkey = %s", device_attr.mqtt_param.mqtt_broken_secretkey);

        nvs_get_u8(my_handle, "decode", &device_attr.mqtt_param.decode);
        ESP_LOGD(TAG,"decode = %d", device_attr.mqtt_param.decode);
    }else{
        device_attr.mqtt_param.mqttver = 0;

        buf_len = sizeof(device_attr.mqtt_param.mqtt_broken_url);
        memset(buf,0,buf_len);
        nvs_get_str(my_handle, "brokerurl", buf, &buf_len);
        strcpy(device_attr.mqtt_param.mqtt_broken_url,buf);
        ESP_LOGI(TAG,"brokerurl = %s", device_attr.mqtt_param.mqtt_broken_url);

        buf_len = sizeof(device_attr.mqtt_param.mqtt_broken_accesskey);
        memset(buf,0,buf_len);
        nvs_get_str(my_handle, "acckey", buf, &buf_len);
        strcpy(device_attr.mqtt_param.mqtt_broken_accesskey,buf);
        ESP_LOGD(TAG,"acckey = %s", device_attr.mqtt_param.mqtt_broken_accesskey);

        buf_len = sizeof(device_attr.mqtt_param.mqtt_broken_secretkey);
        memset(buf,0,buf_len);                    
        nvs_get_str(my_handle, "secretkey", buf, &buf_len);
        strcpy(device_attr.mqtt_param.mqtt_broken_secretkey,buf);
        ESP_LOGD(TAG,"secretkey = %s", device_attr.mqtt_param.mqtt_broken_secretkey);
    }

    nvs_get_u8(my_handle, "mqttwork", &device_attr.mqtt_param.mqttwork);
    ESP_LOGI(TAG,"mqttwork = %d", device_attr.mqtt_param.mqttwork);

    nvs_get_u16(my_handle, "error", &system_state.error_ex);
    ESP_LOGI(TAG,"error_ex = %d", system_state.error_ex);

    nvs_get_u8(my_handle, "s_ota", &device_attr.silent_ota);
    ESP_LOGI(TAG,"silent_ota_flag = %d", device_attr.silent_ota);

    if(device_attr.silent_ota == 1){
        top_led_mode = 2;
        nvs_get_u8(my_handle, "onoff_status", &lamp.onoff_status);
        paramenter_setting_u8("onoff_status", 0);
    }else{
        top_led_mode = 1;
        lamp.onoff_status = 0;
    }
    ESP_LOGI(TAG,"onoff_status = %d", lamp.onoff_status);

    nvs_get_u8(my_handle, "control_mode", &lamp.control_mode);
    ESP_LOGI(TAG,"control_mode = %d", lamp.control_mode);

    nvs_get_u8(my_handle, "brightness_val", &lamp.brightness_val);
    ESP_LOGI(TAG,"brightness_val = %d", lamp.brightness_val);
    lamp_ex.brightness_val = lamp.brightness_val;

    nvs_get_u8(my_handle, "color_temp", &lamp.color_temp);
    ESP_LOGI(TAG,"color_temp = %d", lamp.color_temp);
    lamp_ex.color_temp = lamp.color_temp;

    nvs_get_u16(my_handle, "delay_time", &lamp.delay_time);
    ESP_LOGI(TAG,"delay_time = %d", lamp.delay_time);

    nvs_get_u8(my_handle, "gesture_mode", &user_attr.gesture_mode);
    ESP_LOGI(TAG,"gesture_mode = %d", user_attr.gesture_mode);

    nvs_get_u8(my_handle, "scene_mode", &lamp.scene_mode);
    ESP_LOGI(TAG,"scene_mode = %d", lamp.scene_mode);

    nvs_get_u8(my_handle, "touch_mode", &user_attr.touch_mode);
    ESP_LOGI(TAG,"touch_mode = %d", user_attr.touch_mode);

    nvs_get_u16(my_handle, "study_time1", &learning_duration.mild);
    ESP_LOGI(TAG,"learning_duration mild = %d", learning_duration.mild);
                                                     
    nvs_get_u16(my_handle, "study_time2", &learning_duration.moderate);
    ESP_LOGI(TAG,"learning_duration moderate = %d", learning_duration.moderate);
                                                     
    nvs_get_u16(my_handle, "study_time3", &learning_duration.severe);
    ESP_LOGI(TAG,"learning_duration severe = %d", learning_duration.severe);

    nvs_get_u16(my_handle, "study_time4", &learning_duration.lighting);
    ESP_LOGI(TAG,"learning_duration lighting = %d", learning_duration.lighting);

    nvs_get_u16(my_handle, "study_time5", &learning_duration.now);
    ESP_LOGI(TAG,"learning_duration now = %d", learning_duration.now);
                                                     
    nvs_get_u16(my_handle, "study_time6", &learning_duration.last);
    ESP_LOGI(TAG,"learning_duration last = %d", learning_duration.last);
                                                     
    nvs_get_u16(my_handle, "study_time7", &learning_duration.sum);
    ESP_LOGI(TAG,"learning_duration sum = %d", learning_duration.sum);

    nvs_get_u32(my_handle, "rpt_start", &user_attr.rpt_start);
    ESP_LOGI(TAG,"last report start time = %ld", user_attr.rpt_start);

    buf_len = sizeof(rec_day);
    memset(rec_day,0,buf_len);
    nvs_get_str(my_handle, "rec_day", rec_day, &buf_len);
    ESP_LOGI(TAG,"last record day = %s", rec_day);

    human_state_ex.respiratory = 99;
    human_state_ex.heart_rate = 99;
    human_state_ex.body_movement = 99;
    human_state_ex.body_angle = 99;
    human_state_ex.body_distance = 99;
    human_state_ex.body_status = 99;
    human_state_ex.flow_state = 99;
    human_state_ex.posture_state = 99;
    human_state_ex.activity_freq = 99;
    
    nvs_close(my_handle);

    ESP_LOGI(TAG,"Paramenter init completed");
}
